home *** CD-ROM | disk | FTP | other *** search
- /*
- Grating.c
- This demo shows how to load the clut and put a vignetted grating onto the
- screen. It also saves the image to disk as a PICT file. This demo has been kept
- as simple as possible, to enhance readability, forsaking accurate control of
- time and contrast of the stimulus. The FlickeringGrating demo, on the other
- hand, is somewhat more elaborate and presents a research-grade stimulus if the
- LuminanceRecord.h calibration data are accurate.
-
- For a real experiment there are some refinements you should consider. This demo
- takes a while to create the image on the screen, but you probably want
- frame-accurate timing in your experiment. You could either create the image in
- an offscreen GWorld and copy it to the screen with CopyBitsQuickly. Or you could
- set the clut to uniform gray (every clut entry equal to the background
- luminance) while you're computing the image on-screen, and then load a grayscale
- ramp into the clut when you want the display to start. Similarly you can make
- the image disappear by loading a new image, calling EraseRect(), or loading
- a uniform-gray clut.
-
- Another issue that matters for a real experiment is gamma correction. The
- numbers loaded into the clut are faithfully transformed into voltages by the
- three digital-to-analog converters on your video card, but the resulting
- luminance produced on your monitor will be an approximately parabolic function
- of the video voltage. Apple provides crude gamma correction by means of a
- generic 8-bit gamma table in the video driver, which does make things look
- better, but is not accurately matched to the gamma of your particular monitor,
- which depends on the current settings of its brightness and contrast knobs.
- Furthermore the Apple 8-bit gamma-correction scheme, which simply transforms the
- nominal 8-bit clut value into a new 8-bit clut value, necessarily restricts the
- range of available values, mapping several onto one output value, and omitting
- some output values. In other words your luminances will be 8-bit quantized
- twice, both before and after gamma correction. It is preferable to do gamma
- correction first, without quantization. Therefore I suggest you eliminate
- Apple's gamma correction, by calling GDUncorrectedGamma(), and use the
- Luminance.c package to do gamma correction. That package implements the
- published algorithm of Pelli and Zhang (1991). The FlickeringGrating demo uses
- Luminance.c.
-
- Why not merge main() and Grating()? The call to Require() checks for any needed
- hardware. It is vital that a test for the presence of a needed floating point
- unit be done before entering any routine, e.g. Grating(), that uses the fpu,
- because the THINK C compiler typically produces code that accesses the fpu, to
- save fpu registers, at the beginning of the routine, so any subsequent check
- would be too late.
-
- HISTORY:
- 2/7/93 dgp wrote it, as an answer to questions from Bill Merigan and David Brainard.
- 2/18/93 dgp added fpu test.
- 2/23/93 dgp use new GDOpenWindow1 and GDDisposeWindow1.
- 4/18/93 dgp support directType.
- 7/7/93 dgp prefer screen 1. Added ScrollRect for compatibility with Radius PowerView.
- 10/2/93 dgp added SAVE_PICT to test PixMapToPICT().
- 4/27/94 dgp now ask user whether to save grating as PICT.
- 8/11/94 dgp don't redefine "sin".
- 9/5/94 dgp removed assumption in printf's that int==short.
- 3/20/95 dgp removed SelectWindow() workaround for CW5 SIOUX bug.
- 4/11/95 dgp set and restore display depth and color. Use ChooseScreen().
- 6/30/95 dgp fixed pageRect in call to WindowToEPS. I'm surprised no one reported that
- the grating.eps file was useless. Ok now.
- */
- #include "VideoToolbox.h"
- #if UNIVERSAL_HEADERS
- #include <LowMem.h>
- #else
- #define LMGetMBarHeight() (* (short *) 0x0BAA)
- #define LMSetMBarHeight(MBarHeightValue) ((* (short *) 0x0BAA) = (MBarHeightValue))
- #endif
- void Grating(void);
- #define SIZE 300
-
- void main(void)
- {
- StackGrow(10000);
- Require(gestalt8BitQD);
- Grating();
- }
-
- void Grating(void)
- {
- short i,j,error,clutSize,pixelSize,oldPixelSize,oldIsColor,mode;
- short preferredPixelSize[6]={8,32,16,4,2,1}; // Order of preference
- GDHandle device;
- ColorSpec table[256];
- WindowPtr window,oldPort;
- Rect r;
- double a,fX[SIZE],fY[SIZE];
- char string[100];
- Point pt;
- Boolean savePict=1,saveEPS=1;
- ColorTable **ct;
-
- assert(StackSpace()>5000);
- #if (THINK_C || THINK_CPLUS || SYMANTEC_C)
- console_options.top = 0;
- console_options.left = 0;
- console_options.nrows = 4;
- console_options.ncols = 60;
- printf("\n");
- #elif __MWERKS__
- SIOUXSettings.toppixel=LMGetMBarHeight()+1; // allow for menu bar only
- SIOUXSettings.leftpixel=1;
- SIOUXSettings.rows=4;
- SIOUXSettings.columns=60;
- SIOUXSettings.autocloseonquit=0;
- SIOUXSettings.showstatusline=0;
- SIOUXSettings.asktosaveonclose=0;
- printf("\n");
- #else
- InitGraf(&qd.thePort);
- InitFonts();
- InitWindows();
- InitCursor();
- #endif
- printf("Welcome to Grating.\n");
- GetPort(&oldPort);
-
- /* Find device corresponding to the experimental screen. */
- for(i=8;i>=0;i--){
- device = GetScreenDevice(i);
- if(device!=NULL) break;
- }
- do{
- if(GetScreenDevice(1)!=NULL)i=ChooseScreen(i,"Which screen?");
- else i=0;
- device=GetScreenDevice(i);
- }while(device==NULL);
-
- // Try to get the best possible pixelSize.
- oldPixelSize=(**(**device).gdPMap).pixelSize;
- oldIsColor=TestDeviceAttribute(device,gdDevType);
- if(oldPixelSize!=preferredPixelSize[0] && NewPaletteManager()){
- for(i=0;i<sizeof(preferredPixelSize)/sizeof(*preferredPixelSize);i++){
- if(oldPixelSize==preferredPixelSize[i] && oldIsColor)break;
- mode=HasDepth(device,preferredPixelSize[i],0,0);
- if(mode!=0){
- printf("Changing pixelSize to %d bits, color.\n",(int)preferredPixelSize[i]);
- error=SetDepth(device,preferredPixelSize[i],1<<gdDevType,1); // color
- break;
- }
- }
- }
- pixelSize=(**(**device).gdPMap).pixelSize;
-
- savePict=Choose(savePict,"Save grating to disk as a PICT file?\n",noYes,2);
- saveEPS=Choose(saveEPS,"Save grating to disk as an EPS file?\n",noYes,2);
-
- // Load the clut with a grayscale ramp.
- GDSaveGamma(device);
- GDUncorrectedGamma(device); // Tell the driver to faithfully copy our colors into
- // the clut without any transformation.
- // reverse table matches standard gray color table, which we'll use for PixMapToPICT().
- clutSize=GDClutSize(device);
- for(i=0;i<clutSize;i++){
- table[i].rgb.red=table[i].rgb.green=table[i].rgb.blue
- =(clutSize-1-i)*(long)0xffff/(clutSize-1);
- }
- error=GDSetEntriesByType(device,0,clutSize-1,table);
-
- // Open window and put grating in it.
- window=GDOpenWindow1(device);
- SetPort(window);
- PmBackColor((clutSize-1)/2); // This works because GDOpenWindow1 marked
- EraseRect(&window->portRect); // all the colors as pmExplicit.
- SetRect(&r,0,0,SIZE,SIZE);
- CenterRectInRect(&r,&window->portRect);
- for(i=0;i<SIZE;i++){
- a=(i-SIZE/2)/(SIZE/6.);
- fY[i]=exp(-a*a);
- fX[i]=fY[i]*sin((i-SIZE/2)*(2.0*PI/80.0));
- }
- /*
- Calling ShieldCursor() has two desirable effects, one direct, one indirect.
- Firstly, it keeps the cursor from messing up the grating that we're drawing.
- Secondly, ShieldCursor() is the trap that non-standard QuickDraw devices patch in order
- to find out what parts of the screen have been updated. Thus the call to ShieldCursor()
- will make the grating show up on a Radius PowerView, which drives a video monitor via
- SCSI, and needs to know when to copy new info from the memory buffer to the
- screen device. My understanding is that all QuickDraw drawing operations automatically
- call ShieldCursor() & ShowCursor(), so this is only relevant when we draw bypassing
- QuickDraw, i.e. by calling SetPixelsQuickly or CopyBitsQuickly. CopyWindows() calls
- ShieldCursor and ShowCursor for you.
- */
- pt.h=pt.v=0;
- LocalToGlobal(&pt);
- ShieldCursor(&r,pt);
- for(j=0;j<SIZE;j++){
- unsigned long row[SIZE];
- for(i=0;i<SIZE;i++) row[i]=(clutSize-1)*0.5*(1.0+fY[j]*fX[i]);
- if(pixelSize==16)for(i=0;i<SIZE;i++) row[i]*=1+(1<<5)+(1<<10);
- if(pixelSize==32)for(i=0;i<SIZE;i++) row[i]*=1+(1<<8)+(1UL<<16);
- SetPixelsQuickly(r.left,r.top+j,row,SIZE);
- }
- if(savePict){
- pixelSize=8;
- ct=GetCTable(pixelSize+32); // gray ramp
- PixMapToPICT("grating.pict",((CWindowPtr)window)->portPixMap
- ,&r,pixelSize,ct);
- printf("Image was saved to disk as file “grating.pict”.\n");
- }
- if(saveEPS){
- Rect pageRect;
- SetRect(&pageRect,8.5/2.0*72-((r.right-r.left)/2)
- ,7.0*72+(r.bottom-r.top)
- ,8.5/2*72+((r.right-r.left)/2),7.0*72);
- WindowToEPS((CWindowPtr)window,"grating.eps",&r,&pageRect,0,0,NULL);
- printf("Image was saved to disk as file “grating.eps.\n");
- }
- ShowCursor();
- SetPort((WindowPtr)oldPort);
- printf("Done. Hit return to quit.\n");
- gets(string);
- GDDisposeWindow1(window);
- // restore
- GDRestoreGamma(device);
- GDRestoreDeviceClut(device);
- error=SetDepth(device,oldPixelSize,1<<gdDevType,oldIsColor);
- #if (THINK_C || THINK_CPLUS || SYMANTEC_C)
- abort();
- #elif __MWERKS__
- SIOUXSettings.autocloseonquit=1;
- #endif
- }
-